using System.Configuration;
using System.Web;
using System.Xml;
using System;

/*	-----------------------------------------------------------------------	
	Copyright:	umlungu consulting (pty) ltd
	Author:		Alan Benington	
	Started:	2005-09-08
	Status:		release
	Version:	2.0.0
	Build:		20070621
	License:	GNU General Public License
	-----------------------------------------------------------------------	*/

/*	-----------------------------------------------------------------------	
	Development Notes:
	==================
	20070621:	Jacked from precisionX
	-----------------------------------------------------------------------	*/

namespace umlungu.engineX {
	/// <summary>
	/// The EngineX node class.
	/// <para>The base class for all nodes.</para>
	/// <para>A node represents any level in an EngineX hierarchy</para>
	/// </summary>
	public class x_node : XmlDocument {
		#region Invisible properties
		#endregion

		#region Constants
		private const string DEFAULT_NAME = "x_node";
		private const string DEFAULT_NODE_ID = "node";

		private const string SELECT_NAME = "//{0}[@name='{1}']";
		private const string SELECT_ID = "//{0}[@id='{1}']";
		private const string SELECT_PARENT_NAME = "//{0}[@name='{1}']/{0}[@name='{2}']";
		private const string SELECT_TYPE = "[@type='{0}']";
		private const string SELECT_TYPES = "//{0}[@type='{1}']";
		private const string SELECT_REFERENCE = "//{0}[@reference]";
		private const string SELECT_NOTID = "//{0}[not(@id)]";
		#endregion

		#region Visible properties
		private string type;
		private string reference;

		private string id;
		/// <summary>The id</summary>
		/// <value>ID of the node</value>
		public string ID {
			get { return (id); }
			set { id = value; }
		}

		private string title;
		/// <summary>The title</summary>
		/// <value>Title of the node</value>
		public string Title {
			get { return (title); }
			set { title = value; }
		}

		private string name;
		/// <summary>The name</summary>
		/// <value>name string</value>
		new public string Name {
			get { return (name); }
			set { name = value; }
		}

		private string root = "\\";
		/// <summary>The root directory</summary>
		/// <value>Root of the node</value>
		public string Root {
			get { return (root); }
			set { root = value; }
		}

		private string data = "data";
		/// <summary>The data directory</summary>
		/// <value>Data of the node</value>
		public string Data {
			get { return (data); }
			set { data = value; }
		}
		#endregion

		#region Constructors/Destructors
		/// <overloads>Constructor</overloads>
		/// <summary>Default constructor</summary>
		public x_node() {
			initialise();
		}
		/// <summary>Constructor specifying node ID</summary>
		/// <param name="NodeID">The name of a node element in the node tree</param>
		public x_node(string NodeID) {
			initialise(NodeID);
		}
		/// <summary>Constructor specifying node ID and file to Load</summary>
		/// <param name="NodeID">The name of a node element in the node tree</param>
		/// <param name="FileName">Node definition file</param>
		public x_node(string NodeID, string FileName) {
			initialise(NodeID);
			_Load(FileName);
		}
		/// <summary>Constructor specifying node ID and file to Load + flag to indicate with/without expansion of nodes in tree</summary>
		/// <param name="NodeID">The name of a node element in the node tree</param>
		/// <param name="FileName">Node definition file</param>
		/// <param name="Expand">flad to indicate expansion of the node tree</param>
		public x_node(string NodeID, string FileName, bool Expand) {
			initialise(NodeID);
			_Load(FileName, Expand);
		}
		#endregion

		#region Public methods
		/// <overloads>Loads the node properties from a source</overloads>
		/// <summary>Source is an xml file</summary>
		public void _Load(string filename) {
			_Load(filename, false);
		}
		/// <summary>Source is an xml file</summary>
		public void _Load(string filename, bool expand) {
			Load(filename);
			if (expand) {
				// expand all reference nodes...
			}
		}
		/// <summary>Source is an xml element</summary>
		public void _Load(XmlElement xmlnode) {
			load(xmlnode);
		}
		
		/// <overloads>Saves the node properties to a source</overloads>
		/// <summary>Source is an xml file</summary>
		public void _Save(string filename) {
		}
		
		/// <overloads>Get the xml definition of a node</overloads>
		/// <summary>Node identified by parent type and name and descendant ID</summary>
		public XmlNode GetNode(string type, string name, string descendantID, bool expandflag, bool childflag) {
			string selectParent = String.Format(SELECT_NAME, id, name);
			string selectDescendant = isParentNode(type, selectParent, descendantID)? "" : String.Format(SELECT_ID, id, descendantID);
			return getNode(type, selectParent, selectDescendant, expandflag, childflag);
		}
		/// <summary>Node identified by type and name</summary>
		public XmlNode GetNode(string type, string name, bool expandflag) {
			string select = String.Format(SELECT_NAME, id, name);
			return getNode(type, select, expandflag);
		}
		/// <summary>Node identified by type, parent name and name</summary>
		public XmlNode GetNode(string type, string parent, string name, bool expandflag) {
			string select = String.Format(SELECT_PARENT_NAME, id, parent, name);
			return getNode(type, select, expandflag);
		}

		/// <summary>
		/// Description of this method
		/// </summary>
		public XmlNodeList GetNodes(string type, bool expandflag) {
			return getNodeList(type, expandflag);
		}

		public string GetNodeString(string type, string parent, string name, bool expandflag) {
			string select = String.Format(SELECT_PARENT_NAME, id, parent, name);
			return getNodeString(type, select, expandflag);
		}

		/// <overloads>Loads the xml definition of a node</overloads>
		/// <summary>Node identified by type and name</summary>
		public void _LoadNode(string type, string name, bool expandflag) {
			string select = String.Format(SELECT_NAME, id, name);
			getNode(type, select, expandflag);
		}
		#endregion

		#region Protected methods
		/// <summary>Loads the node properties from an xml element</summary>
		protected void load(XmlElement source) {
			name = source.GetAttribute("name");
			type = source.GetAttribute("type");
			reference = source.GetAttribute("reference");
			title = (source.SelectSingleNode("title") != null) ? source.SelectSingleNode("title").InnerText : "";
		}
		#endregion

		#region Private methods
		/// <overloads>Initialise the object on creation</overloads>
		/// <summary>Initialise with default name and id</summary>
		private void initialise() {
			initialise(DEFAULT_NODE_ID, DEFAULT_NAME);
		}
		/// <summary>Initialise with specified id and default name</summary>
		private void initialise(string nodeID) {
			initialise(nodeID, DEFAULT_NAME);
		}
		/// <summary>Initialise with specified id and name</summary>
		private void initialise(string nodeID, string nodeName) {
			id = nodeID;
			name = nodeName;
		}

		private bool isParentNode(string type, string selParent, string descendantID) {
			string select = String.Concat(selParent, String.Format(SELECT_TYPE, type));
			if (descendantID == "0")
				return true;	// special case
			XmlElement thisel = (XmlElement)SelectSingleNode(select);
			if (thisel == null)
				throw (new x_exception("Error_getNode", String.Concat("Node not found:", select)));
			else
				return (thisel.GetAttribute("id") == descendantID);
		}

		/// <overloads>Get the xml definition of a node</overloads>
		/// <summary>Node identified by type and selection string</summary>
		private XmlNode getNode(string type, string select, bool expandflag) {
			select = String.Concat(select, String.Format(SELECT_TYPE, type));
			XmlNode thisnode = SelectSingleNode(select);
			if (expandflag && thisnode != null)
				expand(thisnode);
			return thisnode;
		}
		/// <summary>Node identified by parent type and selection string and descendant selection string</summary>
		private XmlNode getNode(string type, string selParent, string selDesc, bool expandflag, bool childflag) {
			if (DocumentElement == null)
				throw(new x_exception("Error_getNode", "Source document not loaded"));	
			string select = String.Concat(selParent, String.Format(SELECT_TYPE, type), selDesc);
			XmlNode thisnode = SelectSingleNode(select);
			if (thisnode == null)
				throw(new x_exception("Error_getNode", String.Concat("Node not found:", select)));	
			
			if (!childflag) {
				XmlNodeList nodes = thisnode.SelectNodes("node");
				foreach (XmlNode node in nodes)
					thisnode.RemoveChild(node);
			}
			if (expandflag && thisnode != null)
				expand(thisnode);
			return thisnode;
		}

		/// <overloads>Gets a list oif nodes of a particular type</overloads>
		/// <summary>Gets nodes (does not expand first)</summary>
		/// <param name="type">The type of node identified by the 'type' attribute</param>
		private XmlNodeList getNodeList(string type) {
			return getNodeList(type, false);
		}
		/// <summary>Gets nodes with optional node expansion</summary>
		/// <param name="type">The type of node identified by the 'type' attribute</param>
		/// <param name="expandflag">A flag indicating if nodes are to be expanded</param>
		private XmlNodeList getNodeList(string type, bool expandflag) {
			if (expandflag)
				expand();
			return SelectNodes(String.Format(SELECT_TYPES, type));
		}

		/// <overloads>Expand node references</overloads>
		/// <summary>Use default Root and Data directory values</summary>
		private void expand() {
			expand(DocumentElement, Root, Data);
		}
		/// <summary>Use default Root and Data directory values</summary>
		private void expand(XmlNode thisnode) {
			expand(thisnode, Root, Data);
		}
		/// <summary>Use specified root and data directory values</summary>
		private void expand(XmlNode thisnode, string rootdir, string datadir) {
			XmlNodeList refnodes = thisnode.SelectNodes(String.Format(SELECT_REFERENCE, id));
			XmlDocument refdoc = new XmlDocument();
			foreach (XmlNode refnode in refnodes) {
				refdoc.Load(String.Concat(rootdir, datadir, "\\", ((XmlElement)refnode).GetAttribute("reference")));
				refnode.InnerXml = refdoc.DocumentElement.InnerXml;
				// call expand to recursively expand all nodes...
				expand(refnode, rootdir, datadir);
			}

			// Iterate through all nodes and add a unique, sequential id attribute
			XmlNodeList idnodes = thisnode.SelectNodes(String.Format(SELECT_NOTID, id));
			int i=0;
			foreach (XmlNode idnode in idnodes) {
				((XmlElement)idnode).SetAttribute("id", (i++).ToString());
			}
		}

		private string getNodeString(string type, string select, bool expandflag) {
			return String.Concat(select, String.Format(SELECT_TYPE, type));
		}
		#endregion
	}
}
